/*
 *  EncryptFinal.c
 *
 *  Implementing rsa public key encryption
 *
 *  Created on: April/13/2011
 *          by: Arash Molavi Kakhki <arash@ccs.neu.edu>   
 */

#include <stdio.h>
#include <stdlib.h>
#include <openssl/rsa.h>
#include <openssl/engine.h>

#define rsaKeySize 1024
#define PubKeySize 128

void usage() {
    printf("\n\nusage: ./EncryptFinal [Source (plain)] [Public Key] [Destination (cipher)] \n");
}

int main(int argc, char *argv[]) 
{ 
	if(argc != 4) {
        	usage();
        	exit(1);
    	}

	char *Source;
	char *PubKey;
	char *Destination;

	int CiphrBlockSize;
	int PlainTextSize;
	int PlainBlockSize;
	int MaxBlockSize = PubKeySize - 11;

	FILE *Plaintext;
	FILE *Ciphertext;	

	Source      = argv[1];
	PubKey 	    = argv[2];
	Destination = argv[3];
	
	
/****************************************************************************************************/
/* Reading the public key
/****************************************************************************************************/
	FILE *keyfile = fopen(PubKey, "r"); 
	RSA  *rsa     = PEM_read_RSAPublicKey(keyfile, NULL, NULL, NULL);
	
	
/****************************************************************************************************/
/* Calculating the plaintext size, number of blocks and allocating memory
/****************************************************************************************************/	
	Plaintext  = fopen(Source, "r");

	fseek(Plaintext, 0L, SEEK_END);
	PlainTextSize = ftell(Plaintext);
	fseek(Plaintext, 0L, SEEK_SET);
	
	int NumberOfBlocks = (PlainTextSize/MaxBlockSize);		
	if (PlainTextSize % MaxBlockSize > 0) NumberOfBlocks++;

	unsigned char *unencrypt  = (unsigned char *) malloc(PlainTextSize);
	unsigned char *encrypted  = (unsigned char *) malloc(NumberOfBlocks*PubKeySize);

	printf("\n\nPlaintext's size: %d \n" , PlainTextSize);
	printf("Number of blocks: %d \n" , NumberOfBlocks);
	

/****************************************************************************************************/
/* Encryption process
/****************************************************************************************************/
	printf("\nEncryption started");

	Ciphertext = fopen(Destination , "w");
	
	int TotalCipherSize  = 0;
	int TotalPlainRead   = 0;
	int j;	

	for (j = 1; j < NumberOfBlocks+1; j++){

		PlainBlockSize = fread(unencrypt, 1, MaxBlockSize, Plaintext);
		CiphrBlockSize = RSA_public_encrypt(PlainBlockSize, unencrypt, encrypted, rsa, RSA_PKCS1_PADDING);

		if (CiphrBlockSize == -1){
			printf("\nEncryption failed\n");
			RSA_free(rsa);
			return 0;
	    		}
	    	else{
			printf(".");
			fflush(NULL);
			TotalCipherSize  = TotalCipherSize + CiphrBlockSize;
			TotalPlainRead   = TotalPlainRead  + PlainBlockSize;
			encrypted 	 = encrypted + CiphrBlockSize;
	    		}					
		}

	encrypted = encrypted - TotalCipherSize ;

	fwrite(encrypted, 1, TotalCipherSize, Ciphertext);	

	printf("\nEncryption finished \n");


/****************************************************************************************************/
/****************************************************************************************************/
	fclose(Plaintext);
	fclose(Ciphertext);

	RSA_free(rsa);

    	return 1;
}

